﻿using System;
using System.Collections.Generic;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using BuildingOutLine.BuildingOutLine.LoopSearch;
using BuildingOutLine.BuildingOutLine.Triangulation;
using YArchitech.ProgressBar;
using YArchitech.Revit;
using YJKArchKernel.OutWallSearch;
using YJKArchMethodLibrary.Utils;
using YJKArchMethodLibrary.Utils.Generics;

namespace BuildingOutLine.BuildingOutLine
{
	public class ProfileAnalyzer
	{
		public ProfileAnalyzer(ExternalCommandData commandData)
		{
			this.m_Revit = commandData;
			this.m_Doc = this.m_Revit.Application.ActiveUIDocument.Document;
		}

		public bool GetProfiles(View view, ref List<EdgeInfo> levelProfiels, ref ProgressBarForm progressBar)
		{
			List<Element> list = new List<Element>();
			if (!this.GetElements(ref list, ref progressBar))
			{
				return false;
			}
			Transform identity = Transform.Identity;
			identity.Origin = view.Origin;
			identity.BasisX = view.RightDirection;
			identity.BasisY = view.UpDirection;
			identity.BasisZ = view.ViewDirection;
			Transform inverse = identity.Inverse;
			XYZ viewDirection = view.ViewDirection;
			List<Wall> list2 = new List<Wall>();
			List<Pole2D> poleList = new List<Pole2D>();
			foreach (Element element in list)
			{
				if (element.GetType() == typeof(Wall))
				{
					Wall wall = element as Wall;
                    if ((int)wall.WallType.Kind == 1)
					{
						list2.Add(wall);
						continue;
					}
					if ((wall.Location as LocationCurve).Curve.GetType() == typeof(Arc))
					{
						list2.Add(wall);
						continue;
					}
				}
				List<Solid> elementSolids = this.GetElementSolids(element);
				foreach (Solid solid in elementSolids)
				{
					this.GetSolidProfile(solid, viewDirection, inverse, ref poleList);
				}
			}
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			this.GetCurtainWallAndArcWallProfile(list2, inverse, ref poleList);
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			StructureLoopSearcher structureLoopSearcher = new StructureLoopSearcher(this.m_Revit);
			StructureLoopSearcher.SearchEdgeLocation edgeLocation = StructureLoopSearcher.SearchEdgeLocation.SEL_Outside;
			List<List<Curve>> fullFloorEdgeLoops = structureLoopSearcher.GetFullFloorEdgeLoops(poleList, edgeLocation, false);
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			List<Pole2D> list3 = new List<Pole2D>();
			foreach (List<Curve> list4 in fullFloorEdgeLoops)
			{
				foreach (Curve curve in list4)
				{
					list3.Add(Pole2D.TransformWallToStructure2DPole(curve, ElementId.InvalidElementId, 0.0, 0.0));
				}
			}
			fullFloorEdgeLoops.Clear();
			fullFloorEdgeLoops = structureLoopSearcher.GetFullFloorEdgeLoops(list3, edgeLocation, false);
			List<List<EdgeInfo>> list5 = new List<List<EdgeInfo>>();
			foreach (List<Curve> list6 in fullFloorEdgeLoops)
			{
				List<EdgeInfo> list7 = new List<EdgeInfo>();
				foreach (Curve curve2 in list6)
				{
					EdgeInfo edgeInfo = new EdgeInfo(curve2, false);
					edgeInfo.TransformSelf(identity);
					list7.Add(edgeInfo);
				}
				list5.Add(list7);
			}
			foreach (List<EdgeInfo> collection in list5)
			{
				levelProfiels.AddRange(collection);
			}
			return true;
		}

		private List<List<Wall>> GetWalls(ref List<ElementId> wallLevelIdList)
		{
			List<List<Wall>> list = new List<List<Wall>>();
			AssistFunc.mmToFeet(100.0);
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_Doc);
			ICollection<Element> collection = filteredElementCollector.OfClass(typeof(Wall)).ToElements();
			foreach (Element element in collection)
			{
				Wall wall = element as Wall;
				if (!wall.ParametersMap.Contains("已附着顶部"))
				{
					bool flag = wall.ParametersMap.Contains("Top is Attached");
					if (!flag)
					{
						continue;
					}
				}
				ElementId levelId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
				int num = wallLevelIdList.FindIndex((ElementId item) => item.IntegerValue == levelId.IntegerValue);
				if (num == -1)
				{
					wallLevelIdList.Add(levelId);
					list.Add(new List<Wall>
					{
						wall
					});
				}
				else
				{
					list[num].Add(wall);
				}
			}
			return list;
		}

		private bool GetElements(ref List<Element> elements, ref ProgressBarForm progressBar)
		{
			AssistFunc.mmToFeet(100.0);
			List<ElementId> wallLevelIdList = new List<ElementId>();
			List<List<Wall>> walls = this.GetWalls(ref wallLevelIdList);
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			List<Wall> collection = new List<Wall>();
			this.FilterOutWalls(walls, wallLevelIdList, ref collection);
			elements.AddRange(collection);
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_Doc);
			ICollection<Element> collection2 = filteredElementCollector.OfClass(typeof(FootPrintRoof)).OfCategory(BuiltInCategory.OST_Roofs).ToElements();
			foreach (Element item in collection2)
			{
				elements.Add(item);
			}
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			FilteredElementCollector filteredElementCollector2 = new FilteredElementCollector(this.m_Doc);
			ICollection<Element> collection3 = filteredElementCollector2.OfClass(typeof(ExtrusionRoof)).OfCategory(BuiltInCategory.OST_Roofs).ToElements();
			foreach (Element item2 in collection3)
			{
				elements.Add(item2);
			}
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			FilteredElementCollector filteredElementCollector3 = new FilteredElementCollector(this.m_Doc);
			ICollection<Element> collection4 = filteredElementCollector3.OfClass(typeof(Floor)).OfCategory(BuiltInCategory.OST_Floors).ToElements();
			foreach (Element item3 in collection4)
			{
				elements.Add(item3);
			}
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			FilteredElementCollector filteredElementCollector4 = new FilteredElementCollector(this.m_Doc);
			ICollection<Element> collection5 = filteredElementCollector4.OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_Columns).ToElements();
			foreach (Element item4 in collection5)
			{
				elements.Add(item4);
			}
			progressBar.RefreshProgressBar();
			if (progressBar.m_stopTag)
			{
				return false;
			}
			new FilteredElementCollector(this.m_Doc);
			ICollection<Element> collection6 = filteredElementCollector4.OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_StructuralColumns).ToElements();
			foreach (Element item5 in collection6)
			{
				elements.Add(item5);
			}
			progressBar.RefreshProgressBar();
			return !progressBar.m_stopTag;
		}

		private void FilterOutWalls(List<List<Wall>> walls, List<ElementId> wallLevelIdList, ref List<Wall> outWalls)
		{
			for (int i = 0; i < wallLevelIdList.Count; i++)
			{
				OutWallFilter.Search(this.m_Doc, wallLevelIdList[i], walls[i], ref outWalls, false);
			}
		}

		private List<Solid> GetElementSolids(Element element)
		{
			Options options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
            options.DetailLevel = (ViewDetailLevel)3;
			options.IncludeNonVisibleObjects = true;
			GeometryElement geometryElement;
			if (element.GetType() == typeof(FamilyInstance) || element.GetType() == typeof(Mullion))
			{
				FamilyInstance familyInstance = element as FamilyInstance;
				geometryElement = familyInstance.GetOriginalGeometry(options);
				geometryElement = geometryElement.GetTransformed(familyInstance.GetTransform());
			}
			else
			{
				geometryElement = element.get_Geometry(options);
			}
			List<Solid> list = new List<Solid>();
			if (null == geometryElement)
			{
				return list;
			}
			foreach (GeometryObject geometryObject in geometryElement)
			{
				if (!(geometryObject.GetType() != typeof(Solid)))
				{
					Solid solid = geometryObject as Solid;
					if (solid != null && !Geometry.LessThan(solid.Volume, 0.001))
					{
						list.Add(solid);
					}
				}
			}
			return list;
		}

		private void GetSolidProfile(Solid solid, XYZ direction, Transform transform, ref List<Pole2D> profiles)
		{
			foreach (object obj in solid.Faces)
			{
				Face face = (Face)obj;
				try
				{
					if (face.GetType() == typeof(PlanarFace))
					{
						PlanarFace face2 = face as PlanarFace;
						if (!Geometry.IsParallel(face2.FaceNormal(), direction, 1E-05))
						{
							try
							{
								XYZ xyz = face2.FaceNormal().CrossProduct(direction).Normalize();
								double num = face2.FaceNormal().AngleOnPlaneTo(direction, xyz);
								num = Geometry.formatAngle(num);
								if (Geometry.Greaterthan_Or_Equal(num, Math.PI*.5))
								{
									continue;
								}
							}
							catch (Exception ex)
							{
								string message = ex.Message;
								continue;
							}
						}
						foreach (object obj2 in face.EdgeLoops)
						{
							EdgeArray edgeArray = (EdgeArray)obj2;
							new List<EdgeInfo>();
							foreach (object obj3 in edgeArray)
							{
								Edge edge = (Edge)obj3;
								Curve curve = edge.AsCurve();
								Pole2D pole = null;
								if (this.GetPole(curve, transform, ref pole))
								{
									this.AddPoleToProfile(pole, ref profiles);
								}
							}
						}
					}
				}
				catch (Exception ex2)
				{
					string message2 = ex2.Message;
				}
			}
		}

		private void GetCurtainWallAndArcWallProfile(List<Wall> curtainWalls, Transform transform, ref List<Pole2D> profiles)
		{
			foreach (Wall wall in curtainWalls)
			{
				double num = 0.0;
				double num2 = 0.0;
				this.GetWallElevations(wall, ref num, ref num2);
				Curve curve = (wall.Location as LocationCurve).Curve;
				List<XYZ> list = new List<XYZ>();
				IList<XYZ> list2 = curve.Tessellate();
				foreach (XYZ pt in list2)
				{
					XYZ pt2 = transform.OfPoint(this.To2d(pt) + XYZ.BasisZ * num);
					list.Add(this.To2d(pt2));
					XYZ pt3 = transform.OfPoint(this.To2d(pt) + XYZ.BasisZ * num2);
					list.Add(this.To2d(pt3));
				}
				List<Pole2D> poleList = new List<Pole2D>();
				List<EdgeInfo> list3 = new List<EdgeInfo>();
				TriangulationKit.CalculateTriangulation(list, ref list3);
				foreach (EdgeInfo edgeInfo in list3)
				{
					Curve curve2 = edgeInfo.ConvertTo(this.m_Revit.Application.Application);
					if (!(curve2 == null))
					{
						Pole2D pole = Pole2D.TransformWallToStructure2DPole(curve2, ElementId.InvalidElementId, 0.0, 0.0);
						this.AddPoleToProfile(pole, ref poleList);
					}
				}
				StructureLoopSearcher structureLoopSearcher = new StructureLoopSearcher(this.m_Revit);
				List<List<Curve>> fullFloorEdgeLoops = structureLoopSearcher.GetFullFloorEdgeLoops(poleList, StructureLoopSearcher.SearchEdgeLocation.SEL_Outside, false);
				foreach (List<Curve> list4 in fullFloorEdgeLoops)
				{
					foreach (Curve curve3 in list4)
					{
						Pole2D pole2 = Pole2D.TransformWallToStructure2DPole(curve3, ElementId.InvalidElementId, 0.0, 0.0);
						this.AddPoleToProfile(pole2, ref profiles);
					}
				}
			}
		}

		private void GetWallElevations(Wall wall, ref double bottom, ref double top)
		{
			ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
			Level level = this.m_Doc.GetElement(elementId) as Level;
			bottom = level.Elevation;
			double num = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
			bottom += num;
			ElementId elementId2 = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
			if (elementId2 == ElementId.InvalidElementId)
			{
				double num2 = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
				top = bottom + num2;
				return;
			}
			Level level2 = this.m_Doc.GetElement(elementId2) as Level;
			top = level2.Elevation;
			double num3 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
			top += num3;
		}

		private XYZ To2d(XYZ pt)
		{
			return new XYZ(pt.X, pt.Y, 0.0);
		}

		private void AddPoleToProfile(Pole2D pole, ref List<Pole2D> profile)
		{
			for (int i = 0; i < profile.Count; i++)
			{
				Pole2D pole2D = profile[i];
				if (pole.IsArcPole == pole2D.IsArcPole && !pole.IsArcPole)
				{
					XYZ xyz = null;
					XYZ xyz2 = null;
					Geometry.OverlapType overlapType = Geometry.IsOverlapOfTwoLine(pole.StartPoint, pole.EndPoint, pole2D.StartPoint, pole2D.EndPoint, ref xyz, ref xyz2, false, 1E-09);
					if (overlapType == Geometry.OverlapType._OT_Equality || overlapType == Geometry.OverlapType._OT_SecondIncludeFirst)
					{
						return;
					}
					if (overlapType == Geometry.OverlapType._OT_FirstIncludeSecond)
					{
						this.AddSubEdge(new List<XYZ>
						{
							pole.StartPoint,
							pole.EndPoint,
							pole2D.StartPoint,
							pole2D.EndPoint
						}, pole2D, ref profile);
						return;
					}
					if (overlapType == Geometry.OverlapType._OT_Part)
					{
						profile.RemoveAt(i);
						i--;
						this.AddSubEdge(new List<XYZ>
						{
							pole.StartPoint,
							pole.EndPoint,
							pole2D.StartPoint,
							pole2D.EndPoint
						}, null, ref profile);
						return;
					}
				}
			}
			profile.Add(pole);
		}

		private void AddSubEdge(List<XYZ> points, Pole2D oldPole, ref List<Pole2D> profile)
		{
			points.Sort(new DistanceComparer(new XYZ(-1000.0, -1000.0, 0.0)));
			for (int i = 0; i < points.Count - 1; i++)
			{
				if (!Geometry.Lessthan_Or_Equal(points[i].DistanceTo(points[i + 1]), 0.0026246719160104987))
				{
					Line curve = Line.CreateBound(points[i], points[i + 1]);
					Pole2D pole2D = Pole2D.TransformWallToStructure2DPole(curve, ElementId.InvalidElementId, 0.0, 0.0);
					if (oldPole == null || !oldPole.SameAs(pole2D))
					{
						this.AddPoleToProfile(pole2D, ref profile);
					}
				}
			}
		}

		private bool GetPole(Curve curve, Transform transform, ref Pole2D pole)
		{
			EdgeInfo edgeInfo = new EdgeInfo(curve.GetEndPoint(0), curve.GetEndPoint(1), 0.0);
			edgeInfo.TransformSelf(transform);
			if (Geometry.Lessthan_Or_Equal(edgeInfo.Length(), 0.0026246719160104987))
			{
				return false;
			}
			curve = edgeInfo.ConvertTo(this.m_Revit.Application.Application);
			if (curve == null)
			{
				return false;
			}
			pole = Pole2D.TransformWallToStructure2DPole(curve, ElementId.InvalidElementId, 0.0, 0.0);
			return true;
		}

		private ExternalCommandData m_Revit;

		private Document m_Doc;
	}
}
